package util ;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

/**
 * API for the logging system.
 *
 * @author idan
 */
public class Log
{
	private static final boolean LOG_TO_FILE_BY_DEFAULT = false ;
	private static final boolean LOG_TO_CONSOLE_BY_DEFAULT = true ;
	private static final Level DEFAULT_LOG_LEVEL = Level.ALL ;

	static
	{
		Log.disableDefaultConsoleLogging() ;
	}

	/**
	 * build and configure a new logger.
	 * @param subsystem subsystem for which the logger is built.
	 * @param loggingLevel initial logging level.
	 * @param logToConsole 'true' if output should be logged to console, 'false'
	 * otherwise.
	 * @param logToFile 'true' if output should be logged to a file, 'false'
	 * otherwise.
	 * @throws SecurityException if there is no logging permission.
	 * @throws IOException if the file cannot be opened.
	 * @post logToConsole == true => logger logs to console.
	 * @post logToFile == true => logger logs to file subsystem.toString() +
	 * ".X.log", where X is the log number.
	 * @post logger name is subsystem.toString().
	 * @post logger level is 'loggingLevel'.
	 * @post logging is in simple format.
	 */
	public static void buildLogger(final Subsystem subsystem, final Level loggingLevel,
		final boolean logToConsole, final boolean logToFile) throws SecurityException, IOException
	{
		final Logger log = Logger.getLogger(subsystem.toString()) ;
		final Formatter format = new SimpleFormatter() ;

		if (logToFile)
		{
			Log.setFileLogging(subsystem.toString(), log, format) ;
		}
		if (logToConsole)
		{
			Log.setConsoleLogging(log, format) ;
		}
		log.setLevel(loggingLevel) ;
	}

	/**
	 * @param subsystem subsystem for which the logger is requested.
	 * @return logger belonging to this subsystem.
	 */
	public static Logger getLogger(final Subsystem subsystem)
	{
		return Logger.getLogger(subsystem.toString()) ;
	}

	/**
	 * @param subsystem subsystem for which the logger is built.
	 * @post logger logs to console.
	 * @post logger name is subsystem.toString().
	 * @post logger level is ALL.
	 * @post logging is in simple format.
	 */
	public static void initializeDefaultLogger(final Subsystem subsystem)
	{
		try
		{
			Log.buildLogger(subsystem, Log.DEFAULT_LOG_LEVEL, Log.LOG_TO_CONSOLE_BY_DEFAULT, Log.LOG_TO_FILE_BY_DEFAULT) ;
		}
		catch (final Exception e)
		{
			throw new Bug("file exceptions when there was no request for file", e) ;
		}
	}

	private static void disableDefaultConsoleLogging()
	{
		final Collection<Handler> handlers = Arrays.asList(Logger.getLogger("").getHandlers()) ;
		for (final Handler handler: handlers)
		{
			Logger.getLogger("").removeHandler(handler) ;
		}
	}

	private static void setConsoleLogging(final Logger log, final Formatter format)
	{
		final ConsoleHandler console = new ConsoleHandler() ;
		console.setFormatter(format) ;
		console.setLevel(Log.DEFAULT_LOG_LEVEL) ;
		log.addHandler(console) ;
	}

	private static void setFileLogging(final String filename, final Logger log, final Formatter format)
		throws IOException
	{
		if (filename == null)
		{
			return ;
		}

		final FileHandler outputFile = new FileHandler(filename + ".%g.log") ;
		outputFile.setFormatter(format) ;
		outputFile.setLevel(Log.DEFAULT_LOG_LEVEL) ;
		log.addHandler(outputFile) ;
	}

	/**
	 * set default logging settings.
	 */
	public static void setupDefaultConfig()
	{
		for (final Subsystem subsystem: Subsystem.values())
		{
			Log.initializeDefaultLogger(subsystem) ;
		}
	}
}
